package lte

import (
	"math"

	"gitee.com/thubcc/mobile/utils"
)

const (
	SEQUENCE_NC       = 1600
	SEQUENCE_SEED_LEN = 31
)

const (
	LTE_MAX_NSYMB = 7

	LTE_MAX_PRB = 110
	LTE_NRE     = 12

	LTE_SYMBOL_SZ_MAX = 2048

	LTE_CP_NORM_NSYMB    = 7
	LTE_CP_NORM_SF_NSYMB = (2 * LTE_CP_NORM_NSYMB)
	LTE_CP_NORM_0_LEN    = 160
	LTE_CP_NORM_LEN      = 144

	LTE_CP_EXT_NSYMB    = 6
	LTE_CP_EXT_SF_NSYMB = (2 * LTE_CP_EXT_NSYMB)
	LTE_CP_EXT_LEN      = 512
	LTE_CP_EXT_7_5_LEN  = 1024

	LTE_NOF_SF_X_FRAME   = 10
	LTE_NOF_SLOTS_PER_SF = 2
	LTE_NSLOTS_X_FRAME   = (LTE_NOF_SLOTS_PER_SF * LTE_NOF_SF_X_FRAME)
)

var (
	ZC_MAP         = [3]int{25, 29, 34}
	LTE_SF_LEN_MAX = LTE_SF_LEN(LTE_SYMBOL_SZ_MAX)
)

func LTE_SF_LEN(symbol_sz int) int { return symbol_sz * 15 }

func LTE_CP_NSYMB(cp string) int {
	if cp == "normal" {
		return LTE_CP_NORM_NSYMB
	} else {
		return LTE_CP_EXT_NSYMB
	}
}

func refsignal_cs_nsymbol(l int, cp string, port_id int) int {
	if port_id < 2 {
		if l%2 != 0 {
			return (l/2+1)*LTE_CP_NSYMB(cp) - 3
		} else {
			return (l / 2) * LTE_CP_NSYMB(cp)
		}
	} else {
		return 1 + l*LTE_CP_NSYMB(cp)
	}
}

func refsignal_cs_nof_symbols(port_id int) int {
	if port_id < 2 {
		return 4
	} else {
		return 2
	}
}
func LTE_REFSIGNAL_PILOT_IDX(i, l, nof_prb int) int { return 2*nof_prb*l + i }

func LTE_RE_IDX(nof_prb, symbol_idx, sample_idx int) int {
	return symbol_idx*nof_prb*LTE_NRE + sample_idx
}

func refsignal_cs_v(port_id, ref_symbol_idx int) int {
	v := 0
	if port_id == 0 {
		if ref_symbol_idx%2 == 0 {
			v = 0
		} else {
			v = 3
		}
	} else if port_id == 1 {
		if ref_symbol_idx%2 == 0 {
			v = 3
		} else {
			v = 0
		}
	} else if port_id == 2 {
		if ref_symbol_idx%2 == 0 {
			v = 0
		} else {
			v = 3
		}
	} else if port_id == 3 {
		if ref_symbol_idx%2 == 0 {
			v = 3
		} else {
			v = 0
		}
	}
	return v
}

func LTE_CP_LEN(symbol_sz, c int) int { return int(math.Ceil(float64(c*symbol_sz) / 2048.0)) }

func LTE_CP_LEN_NORM(symbol, symbol_sz int) int {
	if symbol == 0 {
		return LTE_CP_LEN(symbol_sz, LTE_CP_NORM_0_LEN)
	} else {
		return LTE_CP_LEN(symbol_sz, LTE_CP_NORM_LEN)
	}
}

func LTE_CP_LEN_EXT(symbol_sz int) int { return LTE_CP_LEN(symbol_sz, LTE_CP_EXT_LEN) }

func REFSIGNAL_MAX_NUM_SF(nof_prb int) int { return (8 * nof_prb) }

var LTE_SYMBOL_SZ = map[int]int{6: 128}

func x2_init(cp string, cid, ns, l, p int) int64 {
	var N_cp int
	if cp == "normal" {
		N_cp = 1
	} else {
		N_cp = 0
	}
	lp := refsignal_cs_nsymbol(l, cp, 2*p)
	ci := 1024*(7*(ns+1)+lp+1)*(2*cid+1) + 2*cid + N_cp
	return int64(ci)
}

var (
	tx_ifft_plan = make(map[int]*utils.FFTPlan)
	rx_fft_plan  = make(map[int]*utils.FFTPlan)
	LTEPSS       = make([]*PSS, 3)
	PR           = LTEPR()
)

func init() {
	for k, v := range LTE_SYMBOL_SZ {
		tx_ifft_plan[k] = utils.NewIFFT(v)
		rx_fft_plan[k] = utils.NewFFT(v)
	}
	for t := 0; t < 3; t++ {
		LTEPSS[t] = NewPSS(t)
	}
	ScanPssAll = BuildScanPss(12)
}

func ofdm_tx(s utils.Array3, nof_prb int) utils.Array2 {
	sf_symbols := s.D
	symbol_sz := LTE_SYMBOL_SZ[nof_prb]
	tx_ifft := tx_ifft_plan[nof_prb]
	cp_len0 := LTE_CP_LEN_NORM(0, symbol_sz)
	cp_len1 := LTE_CP_LEN_NORM(1, symbol_sz)
	seq := utils.NewArray2d(LTE_NOF_SF_X_FRAME, LTE_SF_LEN(symbol_sz))
	sequences := seq.D
	n := nof_prb * LTE_NRE
	for sf := 0; sf < LTE_NOF_SF_X_FRAME; sf++ {
		for symbidx := 0; symbidx < LTE_CP_NORM_SF_NSYMB; symbidx++ {
			offset := cp_len0 + (symbol_sz+cp_len1)*symbidx
			if symbidx >= LTE_CP_NORM_NSYMB {
				offset += cp_len0 - cp_len1
			}
			tmp := tx_ifft.Buf
			for i := 0; i < symbol_sz; i++ {
				tmp[i] = 0. + 0.i
			}
			fsym := sf_symbols[sf][symbidx]
			hre := n / 2
			copy(tmp[1:hre+1], fsym[hre:])
			copy(tmp[symbol_sz-hre:], fsym[:hre])
			tx_ifft.Execute()
			for i := 0; i < symbol_sz; i++ {
				tmp[i] *= complex(1./float64((4*2*nof_prb)), 0.0)
			}
			copy(sequences[sf][offset:offset+symbol_sz], tmp)
			var cp int
			if symbidx%LTE_CP_NORM_NSYMB == 0 {
				cp = cp_len0
			} else {
				cp = cp_len1
			}
			copy(sequences[sf][offset-cp:offset], tmp[symbol_sz-cp:])
		}
	}
	return seq
}

func ofdm_rx(rfd []complex128, nof_prb int) (rxSymbols utils.Array3) {
	symbol_sz := LTE_SYMBOL_SZ[nof_prb]
	rxSymbols = utils.NewArray3d(LTE_NOF_SF_X_FRAME, LTE_CP_NORM_SF_NSYMB, symbol_sz)
	sf_symbols := rxSymbols.D
	fft, err := td2fd128.Get()
	if err != nil {
		return
	}
	defer td2fd128.Return(fft)
	cp_len0 := LTE_CP_LEN_NORM(0, symbol_sz)
	cp_len1 := LTE_CP_LEN_NORM(1, symbol_sz)
	for sf := 0; sf < LTE_NOF_SF_X_FRAME; sf++ {
		sequences := rfd[1920*sf : 1920*sf+1920]
		for symbidx := 0; symbidx < LTE_CP_NORM_SF_NSYMB; symbidx++ {
			offset := cp_len0 + (symbol_sz+cp_len1)*symbidx
			if symbidx >= LTE_CP_NORM_NSYMB {
				offset += cp_len0 - cp_len1
			}
			copy(fft.Buf[:symbol_sz-3], sequences[offset:offset+symbol_sz-3])
			copy(fft.Buf[symbol_sz-3:], sequences[offset-3:offset])
			fft.Execute()
			copy(sf_symbols[sf][symbidx][:], fft.Buf)
		}
	}
	return
}
func OFDM_rx(rfd []complex128, nof_prb int) (rxSymbols utils.Array3) {
	return ofdm_rx(rfd, nof_prb)
}
